import lodash from 'lodash';
import { getStrValue, hasPropertyIn, hasValue } from './utils';
import { TypeEnum, variableTypeOf } from './typeof';

export interface DataRow {
  [key: string]: any;
}

export type DataSet = DataRow[] | null | undefined;

export type FilterFuc<T = any> = (data: T, index: number) => boolean;

export interface FieldFilter {
  /** 字段名 */
  field: string;
  /** 是否忽略大小写(默认不忽略) */
  ignoreCase?: boolean;
  /** 是否存在当前属性 */
  exists?: boolean;
  /** 是否有值 value !== null && value !== undefined */
  hasValue?: boolean;
  /** 数据类型 */
  type?: TypeEnum;
  /** 是否是空值 */
  isBlank?: boolean;
  /** 等于 */
  eq?: any;
  /** 不等于 */
  neq?: any;
  /** 大于(支持类型: string \ number \ date) */
  gt?: any;
  /** 大于等于(支持类型: string \ number \ date) */
  gte?: any;
  /** 小于(支持类型: string \ number \ date) */
  lt?: any;
  /** 小于等于(支持类型: string \ number \ date) */
  lte?: any;
  /** 在范围内 */
  in?: any[];
  /** 不在范围内 */
  notIn?: any[];
  /** 匹配正则表达式 */
  regexp?: RegExp | string;
  /** 匹配开始字符串 */
  startsWith?: any;
  /** 匹配结束字符串 */
  endsWith?: any;
  /** 是否包含字符串 */
  includes?: any;
  /** 最小长度(包含) */
  minLength?: number;
  /** 最大长度(包含) */
  maxLength?: number;
  /** 自定义过滤规则 */
  filter?: FilterFuc<Field>;
}

export interface FilterConfig {
  /** 是否全局忽略大小写(默认不忽略 false) */
  ignoreCase?: boolean;
  /** 字段过滤配置 */
  fields: FieldFilter[];
}

export type Filter = FilterFuc<DataRow> | FieldFilter | FilterConfig;

export type SortFuc<T = any> = (a: T, b: T) => number;

export interface FieldSort {
  /** 排序字段 */
  field: string;
  /** 排序规则 */
  compare?: SortFuc;
  /** 排序顺序(默认 ASC) */
  by?: 'ASC' | 'DESC';
}

// export interface SortConfig {
// }

export type Sort = SortFuc<DataRow> | FieldSort | FieldSort[];

/**
 * 数据集过滤、排序
 * @param dataSet 数据集
 * @param filter  过滤规则
 * @param sort    排序规则
 */
const filterAndSortDataSet = (dataSet: DataSet, filter: Filter, sort: Sort): DataSet => {
  let res: DataSet = filterDataSet(dataSet, filter);
  if (res && res.length > 0 && sort) {
    res = sortDataSet(res, sort);
  }
  return res;
};

/**
 * 数据集过滤
 * @param dataSet 数据集
 * @param filter  过滤规则
 */
const filterDataSet = (dataSet: DataSet, filter: Filter): DataSet => {
  if (!dataSet || variableTypeOf(dataSet) !== TypeEnum.array || !filter) {
    return dataSet;
  }
  return dataSet.filter((dataRow, index) => {
    if (filter instanceof Function) {
      // FilterFuc<DataRow>
      return filter(dataRow, index);
    }
    let flag = true;
    lodash.forEach(dataRow, (value, field) => {
      if (!flag) return;
      const valueStr = getStrValue(value, true) ?? '';
      const valueStrLowerCase = valueStr.toLocaleLowerCase();
      const fieldFilter: FieldFilter = filter as FieldFilter;
      const filterConfig: FilterConfig = filter as FilterConfig;
      if (filter && fieldFilter.field && fieldFilter.field === field) {
        // FieldFilter
        flag = dataRowFilter({ rowIndex: index, dataRow, field, value, valueStr, valueStrLowerCase }, fieldFilter);
      } else if (filter && variableTypeOf(filterConfig.fields) === TypeEnum.array && filterConfig.fields.length > 0) {
        // FieldFilterConfig
        const fields: FieldFilter[] = filterConfig.fields.filter((f) => f.field === field);
        if (!fields || fields.length <= 0) {
          return;
        }
        fields.forEach((f) => {
          if (!flag) return;
          flag = dataRowFilter({ ignoreCase: filterConfig.ignoreCase, rowIndex: index, dataRow, field, value, valueStr, valueStrLowerCase }, f);
        });
      }
    });
    return flag;
  });
};

interface Field {
  /** 全局配置 - 是否忽略大小写(默认不忽略) */
  ignoreCase?: boolean;
  /** 数据行 index */
  rowIndex: number;
  /** 行数据 */
  dataRow: DataRow;
  /** 字段名 */
  field: string;
  /** 字段值 */
  value: any;
  /** 字段值 - 字符串 */
  valueStr: string;
  /** 字段值 - 小写字符串 */
  valueStrLowerCase: string;
}

/**
 * 过滤数据
 * @param field         字段值
 * @param fieldFilter   过滤配置
 * @return false: 需要被过滤; true: 不需要过滤
 */
const dataRowFilter = (field: Field, fieldFilter: FieldFilter): boolean => {
  let flag = true;
  // 是否存在当前属性
  if (flag && field.dataRow && hasPropertyIn(fieldFilter, 'exists')) {
    if (fieldFilter.exists) {
      flag = hasPropertyIn(field.dataRow, field.field);
    } else {
      flag = !hasPropertyIn(field.dataRow, field.field);
    }
  }
  // 是否有值 value !== null && value !== undefined
  if (flag && field.dataRow && hasPropertyIn(fieldFilter, 'hasValue')) {
    if (fieldFilter.hasValue) {
      flag = hasValue(field.value);
    } else {
      flag = !hasValue(field.value);
    }
  }
  // 数据类型
  if (flag && fieldFilter.type) {
    flag = variableTypeOf(field.value) === fieldFilter.type;
  }
  // 是否是空值
  if (flag && hasPropertyIn(fieldFilter, 'isBlank')) {
    if (fieldFilter.isBlank) {
      flag = !field.valueStr && lodash.trim(field.valueStr).length <= 0;
    } else {
      flag = !!field.valueStr && lodash.trim(field.valueStr).length > 0;
    }
  }
  // 等于
  if (flag && hasPropertyIn(fieldFilter, 'eq')) {
    const value = getValue(field, fieldFilter.eq, fieldFilter.ignoreCase);
    flag = value.fieldValue === value.filterValue;
  }
  // 不等于
  if (flag && hasPropertyIn(fieldFilter, 'neq')) {
    const value = getValue(field, fieldFilter.neq, fieldFilter.ignoreCase);
    flag = value.fieldValue !== value.filterValue;
  }
  // 大于
  if (flag && hasValue(fieldFilter.gt) && hasValue(field.value)) {
    flag = compare(field.value, fieldFilter.gt) > 0;
  }
  // 大于等于
  if (flag && hasValue(fieldFilter.gte) && hasValue(field.value)) {
    flag = compare(field.value, fieldFilter.gte) >= 0;
  }
  // 小于
  if (flag && hasValue(fieldFilter.lt) && hasValue(field.value)) {
    flag = compare(field.value, fieldFilter.lt) < 0;
  }
  // 小于等于
  if (flag && hasValue(fieldFilter.lte) && hasValue(field.value)) {
    flag = compare(field.value, fieldFilter.lte) <= 0;
  }
  // 在范围内
  if (flag && variableTypeOf(fieldFilter.in) === TypeEnum.array && hasValue(field.value)) {
    const fieldValue = getFieldValue(field, fieldFilter.ignoreCase);
    flag = fieldFilter.in!.map((str) => getFilterValue(field, str, fieldFilter.ignoreCase)).indexOf(fieldValue) !== -1;
  }
  // 不在范围内
  if (flag && variableTypeOf(fieldFilter.notIn) === TypeEnum.array && hasValue(field.value)) {
    const fieldValue = getFieldValue(field, fieldFilter.ignoreCase);
    flag = fieldFilter.notIn!.map((str) => getFilterValue(field, str, fieldFilter.ignoreCase)).indexOf(fieldValue) === -1;
  }
  // 匹配正则表达式
  if (flag && hasValue(fieldFilter.regexp) && hasValue(field.value)) {
    if (variableTypeOf(fieldFilter.regexp) === TypeEnum.regexp) {
      flag = (fieldFilter.regexp as RegExp).test(field.valueStr);
    } else if (variableTypeOf(fieldFilter.regexp) === TypeEnum.string) {
      flag = new RegExp(fieldFilter.regexp as string).test(field.valueStr);
    }
  }
  // 匹配开始字符串
  if (flag && variableTypeOf(fieldFilter.startsWith) === TypeEnum.string && hasValue(field.value)) {
    const value = getValue(field, fieldFilter.startsWith, fieldFilter.ignoreCase);
    flag = value.fieldValue.startsWith(value.filterValue);
  }
  // 匹配结束字符串
  if (flag && variableTypeOf(fieldFilter.endsWith) === TypeEnum.string && hasValue(field.value)) {
    const value = getValue(field, fieldFilter.endsWith, fieldFilter.ignoreCase);
    flag = value.fieldValue.endsWith(value.filterValue);
  }
  // 是否包含字符串
  if (flag && variableTypeOf(fieldFilter.includes) === TypeEnum.string && hasValue(field.value)) {
    const value = getValue(field, fieldFilter.includes, fieldFilter.ignoreCase);
    flag = value.fieldValue.includes(value.filterValue);
  }
  // 最小长度
  if (flag && variableTypeOf(fieldFilter.minLength) === TypeEnum.number && hasValue(field.value)) {
    flag = field.valueStr.length <= fieldFilter.minLength!;
  }
  // 最大长度
  if (flag && variableTypeOf(fieldFilter.maxLength) === TypeEnum.number && hasValue(field.value)) {
    flag = field.valueStr.length >= fieldFilter.maxLength!;
  }
  // 自定义过滤规则
  if (flag && fieldFilter.filter instanceof Function) {
    flag = fieldFilter.filter(field, field.rowIndex);
  }
  return flag;
};

/**
 * @param field       字段值
 * @param v           过滤值
 * @param ignoreCase  过滤配置 - 是否忽略大小写
 */
const getValue = (field: Field, v: any, ignoreCase?: boolean): { fieldValue: string; filterValue: string } => {
  let filterValue: string = getStrValue(v, true) ?? '';
  let fieldValue = field.valueStr;
  if (ignoreCase === true) {
    filterValue = filterValue.toLocaleLowerCase();
    fieldValue = field.valueStrLowerCase;
  } else if (ignoreCase === false) {
  } else if (field.ignoreCase === true) {
    filterValue = filterValue.toLocaleLowerCase();
    fieldValue = field.valueStrLowerCase;
  } else if (field.ignoreCase === false) {
  }
  return { fieldValue, filterValue };
};

/**
 * @param field       字段值
 * @param v           过滤值
 * @param ignoreCase  过滤配置 - 是否忽略大小写
 */
const getFilterValue = (field: Field, v: any, ignoreCase?: boolean): string => {
  let filterValue: string = getStrValue(v, true) ?? '';
  if (ignoreCase === true) {
    filterValue = filterValue.toLocaleLowerCase();
  } else if (ignoreCase === false) {
  } else if (field.ignoreCase === true) {
    filterValue = filterValue.toLocaleLowerCase();
  } else if (field.ignoreCase === false) {
  }
  return filterValue;
};

/**
 * @param field       字段值
 * @param ignoreCase  过滤配置 - 是否忽略大小写
 */
const getFieldValue = (field: Field, ignoreCase?: boolean): string => {
  let fieldValue = field.valueStr;
  if (ignoreCase === true) {
    fieldValue = field.valueStrLowerCase;
  } else if (ignoreCase === false) {
  } else if (field.ignoreCase === true) {
    fieldValue = field.valueStrLowerCase;
  } else if (field.ignoreCase === false) {
  }
  return fieldValue;
};

/**
 * 数据集排序(不影响原数组)
 * @param dataSet 数据集
 * @param sort    排序规则
 */
const sortDataSet = (dataSet: DataSet, sort: Sort): DataSet => {
  if (!dataSet || variableTypeOf(dataSet) !== TypeEnum.array || !sort) {
    return dataSet;
  }
  const copyArray: DataRow[] = dataSet.concat([]);
  return copyArray.sort((a: DataRow, b: DataRow) => {
    let sortRes = 0;
    if (sort instanceof Function) {
      // SortFuc<DataRow>
      sortRes = sort(a, b);
    } else if (variableTypeOf(sort) === TypeEnum.object && (sort as FieldSort).field) {
      // FieldSort
      const fieldSort: FieldSort = sort as FieldSort;
      sortRes = fieldSortByCompare(a, b, fieldSort);
    } else if (variableTypeOf(sort) === TypeEnum.array && (sort as FieldSort[]).length > 0) {
      // FieldSort[]
      const fieldSorts: FieldSort[] = sort as FieldSort[];
      fieldSorts.forEach((fieldSort) => {
        if (sortRes !== 0) return;
        sortRes = fieldSortByCompare(a, b, fieldSort);
      });
    }
    return sortRes;
  });
};

const fieldSortByCompare = (a: DataRow, b: DataRow, fieldSort: FieldSort) => {
  let sortRes: number;
  const by = fieldSort.by ?? 'ASC';
  const aValue: any = a ? a[fieldSort.field] : undefined;
  const bValue: any = b ? b[fieldSort.field] : undefined;
  if (fieldSort.compare instanceof Function) {
    sortRes = fieldSort.compare(aValue, bValue);
  } else {
    sortRes = compare(aValue, bValue);
  }
  if (by === 'DESC' && sortRes !== 0) {
    sortRes *= -1;
  }
  return sortRes;
};

// undefined、null、hasValue 大小定义 undefined < null < hasValue
const compare: SortFuc = (a, b) => {
  let aNumber: number | null = null;
  let bNumber: number | null = null;
  if (!hasValue(a) || !hasValue(b)) {
    aNumber = a === undefined ? 0 : a === null ? 1 : 2;
    bNumber = b === undefined ? 0 : b === null ? 1 : 2;
  } else if (variableTypeOf(a) === TypeEnum.date && variableTypeOf(b) === TypeEnum.date) {
    aNumber = (a as Date).getTime();
    bNumber = (b as Date).getTime();
  } else if (variableTypeOf(a) === TypeEnum.number && variableTypeOf(b) === TypeEnum.number) {
    aNumber = a as number;
    bNumber = b as number;
  }
  if (aNumber !== null && bNumber !== null) {
    return aNumber > bNumber ? 1 : aNumber < bNumber ? -1 : 0;
  }
  const aStrValue = getStrValue(a, true) ?? '';
  const bStrValue = getStrValue(b, true) ?? '';
  return aStrValue.localeCompare(bStrValue);
};

export { filterAndSortDataSet, filterDataSet, sortDataSet };

// TODO: 需要测试
